use serde::{Deserialize, Serialize};

use crate::database::DataSource;

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
pub struct DataTransformSqlUnit {
    pub query: String,
    pub insert: String,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
pub struct DataTransformValuesUnit {
    pub table: String,
    pub wheres: String,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
pub struct DataTransformStructUnit {
    pub tables: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(tag = "type")]
pub enum DataTransformUnit {
    #[serde(rename = "values")]
    Values(DataTransformValuesUnit),
    #[serde(rename = "struct")]
    Struct(DataTransformStructUnit),
    #[serde(rename = "sql")]
    Sql(DataTransformSqlUnit),
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
pub struct DataTransformOptions {
    pub ignore_errs: bool,
    pub ignore_id: bool,
}

#[async_trait::async_trait]
pub trait DataTransformer {
    /// 根据数据库获取到元数据
    async fn transform(
        &self,
        source: &DataSource,
        target: &DataSource,
        units: Vec<DataTransformUnit>,
        options: DataTransformOptions,
    ) -> anyhow::Result<()>;
}

impl DataTransformUnit {
    pub fn values(table: &String, wheres: &String) -> anyhow::Result<DataTransformUnit> {
        Ok(DataTransformUnit::Values(DataTransformValuesUnit {
            table: table.clone(),
            wheres: wheres.clone(),
        }))
    }

    pub fn structs(tables: &Vec<String>) -> anyhow::Result<DataTransformUnit> {
        Ok(DataTransformUnit::Struct(DataTransformStructUnit {
            tables: tables.clone(),
        }))
    }

    pub fn sql(query: &String, insert: &String) -> anyhow::Result<DataTransformUnit> {
        Ok(DataTransformUnit::Sql(DataTransformSqlUnit {
            query: query.clone(),
            insert: insert.clone(),
        }))
    }
}

impl DataTransformOptions {
    pub fn new_with_ignore(ignore_errs: bool, ignore_id: bool) -> Self {
        Self {
            ignore_errs,
            ignore_id,
        }
    }
}
